#include "ShaderProgram.h"
#include "Texture2D.h"
#include "VertexArray.h"
#include "VertexBuffer.h"
#include "GlfwWindow.h"
#include "glm/ext/matrix_transform.hpp"
#include "glm/fwd.hpp"

#include <iostream>
using namespace std;

int main(int argc, char *argv[])
{
    GlfwWindowManager window(1200, 900);
    if (!window.initWindow("LearnOpenGL")) {
        std::cout << "Failed to create GLFW window" << std::endl;
    }

    float vertices[] = {-0.5f, -0.5f, -0.5f, 0.0f,  0.0f,  -1.0f, 0.0f, 0.0f, 0.5f,  -0.5f, -0.5f, 0.0f,  0.0f,  -1.0f, 1.0f, 0.0f,
                        0.5f,  0.5f,  -0.5f, 0.0f,  0.0f,  -1.0f, 1.0f, 1.0f, 0.5f,  0.5f,  -0.5f, 0.0f,  0.0f,  -1.0f, 1.0f, 1.0f,
                        -0.5f, 0.5f,  -0.5f, 0.0f,  0.0f,  -1.0f, 0.0f, 1.0f, -0.5f, -0.5f, -0.5f, 0.0f,  0.0f,  -1.0f, 0.0f, 0.0f,

                        -0.5f, -0.5f, 0.5f,  0.0f,  0.0f,  1.0f,  0.0f, 0.0f, 0.5f,  -0.5f, 0.5f,  0.0f,  0.0f,  1.0f,  1.0f, 0.0f,
                        0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  1.0f, 1.0f, 0.5f,  0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  1.0f, 1.0f,
                        -0.5f, 0.5f,  0.5f,  0.0f,  0.0f,  1.0f,  0.0f, 1.0f, -0.5f, -0.5f, 0.5f,  0.0f,  0.0f,  1.0f,  0.0f, 0.0f,

                        -0.5f, 0.5f,  0.5f,  -1.0f, 0.0f,  0.0f,  1.0f, 0.0f, -0.5f, 0.5f,  -0.5f, -1.0f, 0.0f,  0.0f,  1.0f, 1.0f,
                        -0.5f, -0.5f, -0.5f, -1.0f, 0.0f,  0.0f,  0.0f, 1.0f, -0.5f, -0.5f, -0.5f, -1.0f, 0.0f,  0.0f,  0.0f, 1.0f,
                        -0.5f, -0.5f, 0.5f,  -1.0f, 0.0f,  0.0f,  0.0f, 0.0f, -0.5f, 0.5f,  0.5f,  -1.0f, 0.0f,  0.0f,  1.0f, 0.0f,

                        0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  1.0f, 0.0f, 0.5f,  0.5f,  -0.5f, 1.0f,  0.0f,  0.0f,  1.0f, 1.0f,
                        0.5f,  -0.5f, -0.5f, 1.0f,  0.0f,  0.0f,  0.0f, 1.0f, 0.5f,  -0.5f, -0.5f, 1.0f,  0.0f,  0.0f,  0.0f, 1.0f,
                        0.5f,  -0.5f, 0.5f,  1.0f,  0.0f,  0.0f,  0.0f, 0.0f, 0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  1.0f, 0.0f,

                        -0.5f, -0.5f, -0.5f, 0.0f,  -1.0f, 0.0f,  0.0f, 1.0f, 0.5f,  -0.5f, -0.5f, 0.0f,  -1.0f, 0.0f,  1.0f, 1.0f,
                        0.5f,  -0.5f, 0.5f,  0.0f,  -1.0f, 0.0f,  1.0f, 0.0f, 0.5f,  -0.5f, 0.5f,  0.0f,  -1.0f, 0.0f,  1.0f, 0.0f,
                        -0.5f, -0.5f, 0.5f,  0.0f,  -1.0f, 0.0f,  0.0f, 0.0f, -0.5f, -0.5f, -0.5f, 0.0f,  -1.0f, 0.0f,  0.0f, 1.0f,

                        -0.5f, 0.5f,  -0.5f, 0.0f,  1.0f,  0.0f,  0.0f, 1.0f, 0.5f,  0.5f,  -0.5f, 0.0f,  1.0f,  0.0f,  1.0f, 1.0f,
                        0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  1.0f, 0.0f, 0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  1.0f, 0.0f,
                        -0.5f, 0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  0.0f, 0.0f, -0.5f, 0.5f,  -0.5f, 0.0f,  1.0f,  0.0f,  0.0f, 1.0f};

    VertexArray        vao;
    VertexBuffer       vbo(vertices, sizeof(vertices));
    VertexBufferLayout layout(8 * sizeof(float));
    layout.addAttribute(0, 3, GL_FLOAT, 0);
    layout.addAttribute(1, 3, GL_FLOAT, 3 * sizeof(float));
    layout.addAttribute(2, 2, GL_FLOAT, 6 * sizeof(float));
    vao.addBuffer(vbo, layout);

    VertexArray lightVao;
    vbo.bind();
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void *)0);
    glEnableVertexAttribArray(0);

    Texture2D texture1("./textures/container2.png");
    Texture2D texture2("./textures/container2_specular.png");
    texture1.bind(0);
    texture2.bind(1);
    ShaderProgram lightingShader("./shaders/example_light/color_vertex.shader", "./shaders/example_light/color_fragment.shader");
    ShaderProgram lightCubeShader("./shaders/example_light/light_cube_vertex.shader", "./shaders/example_light/light_cube_fragment.shader");

    glm::vec3 lightPos(1.2f, 1.0f, 2.0f);
    glm::vec3 lightColor(1.0f, 1.0f, 1.0f);

    // 启用深度测试
    glEnable(GL_DEPTH_TEST);
    glClearColor(0.2f, 0.2f, 0.3f, 1.0f);
    window.loopRender([&]() {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        auto cameraPos = window.getCameraPos();
        lightingShader.bind();
        lightingShader.setUniform3f("light.position", 1.2f, 1.0f, 2.0f);
        lightingShader.setUniform3f("light.ambient", 0.2f * lightColor.r, 0.2f * lightColor.g, 0.2f * lightColor.b);
        lightingShader.setUniform3f("light.diffuse", 0.5f * lightColor.r, 0.5f * lightColor.g, 0.5f * lightColor.b);
        lightingShader.setUniform3f("light.specular", 1.0f * lightColor.r, 1.0f * lightColor.g, 1.0f * lightColor.b);

        lightingShader.setUniformMat4fv("u_proj", window.getProj());
        lightingShader.setUniformMat4fv("u_view", window.getView());
        lightingShader.setUniformMat4fv("u_model", glm::mat4(1.0f));

        lightingShader.setUniformVec3f("u_viewPos", cameraPos);
        lightingShader.setUniform1i("material.diffuse", 0);
        lightingShader.setUniform1i("material.specular", 1);
        lightingShader.setUniform1f("material.shininess", 32.0f);
        vao.bind();
        glDrawArrays(GL_TRIANGLES, 0, 36);

        lightCubeShader.bind();
        lightCubeShader.setUniformMat4fv("u_proj", window.getProj());
        lightCubeShader.setUniformMat4fv("u_view", window.getView());

        glm::mat4 model = glm::translate(glm::mat4(1.0f), lightPos);
        model           = glm::scale(model, glm::vec3(0.2f));
        lightCubeShader.setUniformVec3f("u_color", lightColor);
        lightCubeShader.setUniformMat4fv("u_model", model);

        lightVao.bind();
        glDrawArrays(GL_TRIANGLES, 0, 36);
    });
    return 0;
}